/**
 * Copyright [2010] Delta Air Lines, Inc. All Rights Reserved Access,
 * Modification, or Use Prohibited Without Express Permission of Delta Air
 * Lines.
 */
package com.delta.rafg.core.database.impl;


import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.delta.rafg.core.database.DataHandlerOptions;
import com.delta.rafg.core.exception.RAFGException;


/**
 * @author RAFG Development Team (D9127520)
 */
public class ReferenceDataHandlerImpl extends ADataHandler {

    /**
     *
     */
    private static final long serialVersionUID = 8396078869617955897L;

    /**
    *
    */
    private final String driverClassName = "oracle.jdbc.driver.OracleDriver";

    private final Map<String, BigDecimal> tableChangeMap =
        new java.util.Hashtable<String, BigDecimal>();

    
    /**
     * When tables in the reference database are updated, a ORACLE specific
     * pseudo-column ORA_ROWSCN is updated. This pseudo-column is used to detect
     * when a row in a table has been updated.
     * @return changed tables
     * @throws RAFGException RAFGException
     */
    @Override
    public String[] getChangedTables() throws RAFGException {

        List<String> changedTablesLst = null;
        Statement stmt = null;
        try {
            final Connection conn = getConnection();
            stmt = conn.createStatement();
            final String[] monitoredTables = getMonitoredTables();
            for (final String table : monitoredTables) {
                final ResultSet rs =
                    stmt.executeQuery("SELECT MAX(ORA_ROWSCN) FROM " + table);
                // only fetching one row.
                if (rs.next()) {
                    final BigDecimal lastUpdatedInDb = rs.getBigDecimal(1);
                    final BigDecimal lastUpdate = tableChangeMap.get(table);
                    if (lastUpdate == null
                        || lastUpdate.compareTo(lastUpdatedInDb) < 0) {
                        // lastUpdatedInDb could be null too if the table
                        // never has been updated with any data.
                        if (lastUpdatedInDb != null) {
                            if (changedTablesLst == null) {
                                changedTablesLst = new ArrayList<String>();
                            }
                            changedTablesLst.add(table);
                            tableChangeMap.put(table, lastUpdatedInDb);
                        }
                    }
                }
            }
        } catch (final SQLException e) {
            throw new RAFGException(e);
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (final SQLException e) {
                    throw new RAFGException(e);
                }
            }
        }
        if (changedTablesLst == null) {
            return null;
        } else {
            return changedTablesLst
                .toArray(new String[changedTablesLst.size()]);
        }
    }

    /**
     *
     */
    @Override
    public void initialize(final DataHandlerOptions options)
        throws RAFGException {

        if (options.getDriverClassName() == null) {
            options.setDriverClassName(driverClassName);
        }
        super.initialize(options);
        try {
        	lookupSchemaTables();
        } catch(Exception e) {
        	e.printStackTrace();
        }
    }

    /**
     * Look up all tables within any schemas defined to be monitored and add
     * them to the monitored tables list.
     * @throws RAFGException RAFGExceptino
     */
    private void lookupSchemaTables() throws RAFGException {

        final Connection conn = getConnection();
        if (monitoredSchemas != null) {
            try {
                for (final String schema : monitoredSchemas) {
                    final DatabaseMetaData dbmd = conn.getMetaData();
                    // Specify the type of object; in this case we want tables
                    final String[] types = { "TABLE" };
                    final ResultSet resultSet =
                        dbmd.getTables(null, schema, "%", types);
                    if (monitoredTables == null) {
                        monitoredTables = new ArrayList<String>();
                    }
                    // Get the table names
                    while (resultSet.next()) {
                        // Get the table name
                        final String tableName =
                            resultSet.getString("TABLE_NAME");
                        // Get the table's catalog and schema names (if any)
                        final String tableSchema =
                            resultSet.getString("TABLE_SCHEM");
                        if (!monitoredTables.contains(tableSchema + "."
                            + tableName)) {
                            if (excludedTables == null
                                || !excludedTables.contains(tableSchema + "."
                                    + tableName)) {
                            	//ignore tables in the oracle recycle bin
                            	if (!tableName.startsWith("BIN$")) {
	                                monitoredTables.add(tableSchema + "."
	                                    + tableName);
                            	}
                            }
                        }
                    }
                }
            } catch (final java.sql.SQLException e) {
                throw new RAFGException(e);
            }
        }
    }
}
